import tkinter as tk
from tkinter import ttk, messagebox, simpledialog
import time
import threading
import winsound  # Windows系统声音
import platform
import os
from datetime import datetime
import json  # 新增导入
import logging

# 配置日志
logging.basicConfig(
    level=logging.INFO,    # DEBUG INFO WARRING ERROR CRITICAL
    format='%(asctime)s - %(levelname)s - %(message)s',
    filename='pomodoro_debug.log',
    filemode='w'
)
logger = logging.getLogger(__name__)

class 番茄计时器:
    def __init__(self, 根窗口):
        self.根窗口 = 根窗口
        self.根窗口.title("番茄闹钟 - Pomodoro Timer")
        self.根窗口.geometry("400x650+800+200")
        self.根窗口.resizable(False, False)
        
        # 设置窗口图标和样式
        self.根窗口.configure(bg='#2C3E50')
        
        # 计时器状态
        self.是否运行中 = False
        self.是否暂停 = False
        self.是否休息 = False
        self.剩余时间 = 25 * 60  # 默认25分钟
        self.总时间 = 25 * 60
        
        # 线程管理
        self.计时器线程 = None
        self.线程锁 = threading.Lock()
        self.停止事件 = threading.Event()
        
        # 休眠检测
        self.上次更新时间 = time.time()
        self.休眠检测间隔 = 5  # 每5秒检测一次休眠
        
        # 统计数据
        self.番茄计数 = 0
        self.总工作时间 = 0
        self.会话开始时间 = None
        
        # 时间设置（分钟）
        self.工作时長 = 25
        self.短休息时长 = 5
        self.长休息时长 = 15
        
        # 记录存储
        self.记录文件 = "pomodoro_records.json"
        self.记录列表 = []
        self.记录锁 = threading.Lock()
        self.加载记录()
        
        # 窗口管理
        self.打开窗口列表 = []
        
        self.设置界面()
        self.更新显示()
        
        # 绑定窗口关闭事件
        self.根窗口.protocol("WM_DELETE_WINDOW", self.窗口关闭处理)
        
        # 启动UI更新定时器
        self.启动UI更新定时器()
    
    def 启动UI更新定时器(self):
        """启动UI更新定时器，定期检查休眠状态"""
        if not self.停止事件.is_set():
            try:
                logger.debug("执行UI更新定时器检查")
                self.检测休眠状态()
                self._ui_update_id = self.根窗口.after(1000, self.启动UI更新定时器)  # 每秒检查一次
                logger.debug("已安排下一次UI更新检查")
            except Exception as e:
                logger.error(f"UI更新定时器异常: {e}")
                self.停止事件.set()
    
    def 检测休眠状态(self):
        """检测是否从休眠状态恢复"""
        当前时间 = time.time()
        时间差 = 当前时间 - self.上次更新时间
        
        # 如果时间差超过休眠检测间隔的2倍且小于1小时，认为发生了休眠
        if (self.休眠检测间隔 * 2 < 时间差 < 3600 and 
            self.是否运行中 and not self.是否暂停):
            logger.info(f"检测到休眠恢复，时间差: {时间差}秒")
            with self.线程锁:
                self.处理休眠恢复(时间差)
        
        self.上次更新时间 = 当前时间
    
    def 处理休眠恢复(self, 休眠时长):
        """处理从休眠状态恢复"""
        try:
            # 计算休眠期间应该减少的时间
            休眠期间流逝时间 = int(休眠时长)
            
            # 更新剩余时间
            self.剩余时间 = max(0, self.剩余时间 - 休眠期间流逝时间)
            
            # 如果时间已经到了，触发计时完成
            if self.剩余时间 <= 0:
                self.剩余时间 = 0
                self.根窗口.after(0, self.计时完成)
            
            # 立即更新显示
            self.根窗口.after(0, self.更新显示)
            
        except Exception as e:
            print(f"处理休眠恢复时出错: {e}")
    
    def 窗口关闭处理(self):
        """程序关闭时的清理工作"""
        # 设置停止标志
        self.停止事件.set()
        self.是否运行中 = False
        self.是否暂停 = False
        
        # 取消所有待处理的UI更新
        try:
            self.根窗口.after_cancel(self._ui_update_id)
        except:
            pass
        
        # 关闭所有打开的子窗口
        for 窗口 in self.打开窗口列表[:]:
            try:
                窗口.destroy()
            except:
                pass
        
        # 等待线程结束
        if self.计时器线程 and self.计时器线程.is_alive():
            self.计时器线程.join(timeout=0.5)
            if self.计时器线程.is_alive():
                # 如果线程仍在运行，强制终止
                import ctypes
                try:
                    thread_id = self.计时器线程.ident
                    if thread_id:
                        ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, ctypes.py_object(SystemExit))
                except:
                    pass
        
        # 确保主窗口关闭
        try:
            self.根窗口.destroy()
        except:
            pass
        
        # 强制退出程序
        os._exit(0)
        
    def 加载记录(self):
        """加载存储的记录"""
        try:
            if os.path.exists(self.记录文件):
                with open(self.记录文件, 'r', encoding='utf-8') as f:
                    self.记录列表 = json.load(f).get('records', [])
        except Exception as e:
            print(f"加载记录失败: {e}")
            self.记录列表 = []
            
    def 保存记录(self):
        """保存记录到文件"""
        try:
            with self.记录锁:
                # 创建临时文件，避免写入过程中程序崩溃导致文件损坏
                临时文件 = self.记录文件 + '.tmp'
                with open(临时文件, 'w', encoding='utf-8') as f:
                    json.dump({'records': self.记录列表}, f, ensure_ascii=False, indent=2)
                
                # 原子性替换文件
                if os.path.exists(self.记录文件):
                    os.replace(临时文件, self.记录文件)
                else:
                    os.rename(临时文件, self.记录文件)
        except Exception as e:
            print(f"保存记录失败: {e}")
        
    def 设置界面(self):
        # 标题
        标题框架 = tk.Frame(self.根窗口, bg='#2C3E50')
        标题框架.pack(pady=20)
        
        标题标签 = tk.Label(
            标题框架, 
            text="🍅 番茄闹钟", 
            font=("Arial", 24, "bold"),
            fg='#ECF0F1',
            bg='#2C3E50'
        )
        标题标签.pack()
        
        # 状态显示
        self.状态标签 = tk.Label(
            self.根窗口,
            text="工作时间",
            font=("Arial", 14),
            fg='#3498DB',
            bg='#2C3E50'
        )
        self.状态标签.pack(pady=5)
        
        # 时间显示
        self.时间标签 = tk.Label(
            self.根窗口,
            text="25:00",
            font=("Digital-7", 72, "bold"),
            fg='#E74C3C',
            bg='#2C3E50'
        )
        self.时间标签.pack(pady=20)
        
        # 进度条
        self.进度条 = ttk.Progressbar(
            self.根窗口,
            length=300,
            mode='determinate',
            style="TProgressbar"
        )
        self.进度条.pack(pady=10)
        
        # 统计信息
        统计框架 = tk.Frame(self.根窗口, bg='#34495E')
        统计框架.pack(pady=10, padx=20, fill='x')
        
        self.番茄标签 = tk.Label(
            统计框架,
            text=f"完成番茄: {self.番茄计数}",
            font=("Arial", 12),
            fg='#ECF0F1',
            bg='#34495E'
        )
        self.番茄标签.pack(side='left', padx=20, pady=5)
        
        self.总时间标签 = tk.Label(
            统计框架,
            text=f"总工作时间: {self.总工作时间}分钟",
            font=("Arial", 12),
            fg='#ECF0F1',
            bg='#34495E'
        )
        self.总时间标签.pack(side='right', padx=20, pady=5)
        
        # 控制按钮
        按钮框架 = tk.Frame(self.根窗口, bg='#2C3E50')
        按钮框架.pack(pady=20)
        
        self.开始按钮 = tk.Button(
            按钮框架,
            text="开始",
            command=self.开始计时,
            font=("Arial", 12),
            bg='#27AE60',
            fg='white',
            width=8,
            height=2,
            relief='flat',
            cursor='hand2'
        )
        self.开始按钮.grid(row=0, column=0, padx=5)
        
        self.暂停按钮 = tk.Button(
            按钮框架,
            text="暂停",
            command=self.暂停计时,
            font=("Arial", 12),
            bg='#F39C12',
            fg='white',
            width=8,
            height=2,
            relief='flat',
            cursor='hand2',
            state='disabled'
        )
        self.暂停按钮.grid(row=0, column=1, padx=5)
        
        self.重置按钮 = tk.Button(
            按钮框架,
            text="重置",
            command=self.重置计时器,
            font=("Arial", 12),
            bg='#E74C3C',
            fg='white',
            width=8,
            height=2,
            relief='flat',
            cursor='hand2'
        )
        self.重置按钮.grid(row=0, column=2, padx=5)
        
        # 查看记录按钮
        self.查看记录按钮 = tk.Button(
            按钮框架,
            text="查看记录",
            command=self.查看记录,
            font=("Arial", 12),
            bg='#3498DB',
            fg='white',
            width=8,
            height=2,
            relief='flat',
            cursor='hand2'
        )
        self.查看记录按钮.grid(row=0, column=3, padx=5, pady=5)
        
        # 设置区域
        设置框架 = tk.LabelFrame(
            self.根窗口,
            text="时间设置（分钟）",
            font=("Arial", 10),
            fg='#ECF0F1',
            bg='#34495E'
        )
        设置框架.pack(pady=10, padx=20, fill='x')
        
        # 工作时间设置
        工作时间框架 = tk.Frame(设置框架, bg='#34495E')
        工作时间框架.pack(pady=5)
        
        tk.Label(
            工作时间框架,
            text="工作时间:",
            font=("Arial", 10),
            fg='#ECF0F1',
            bg='#34495E'
        ).pack(side='left', padx=5)
        
        self.工作时间变量 = tk.StringVar(value="25")
        工作时间输入框 = tk.Spinbox(
            工作时间框架,
            from_=1,
            to=60,
            textvariable=self.工作时间变量,
            width=5,
            font=("Arial", 10)
        )
        工作时间输入框.pack(side='left')
        
        # 短休息设置
        短休息框架 = tk.Frame(设置框架, bg='#34495E')
        短休息框架.pack(pady=5)
        
        tk.Label(
            短休息框架,
            text="短休息:",
            font=("Arial", 10),
            fg='#ECF0F1',
            bg='#34495E'
        ).pack(side='left', padx=5)
        
        self.短休息变量 = tk.StringVar(value="5")
        短休息输入框 = tk.Spinbox(
            短休息框架,
            from_=1,
            to=30,
            textvariable=self.短休息变量,
            width=5,
            font=("Arial", 10)
        )
        短休息输入框.pack(side='left')
        
        # 长休息设置
        长休息框架 = tk.Frame(设置框架, bg='#34495E')
        长休息框架.pack(pady=5)
        
        tk.Label(
            长休息框架,
            text="长休息:",
            font=("Arial", 10),
            fg='#ECF0F1',
            bg='#34495E'
        ).pack(side='left', padx=5)
        
        self.长休息变量 = tk.StringVar(value="15")
        长休息输入框 = tk.Spinbox(
            长休息框架,
            from_=1,
            to=60,
            textvariable=self.长休息变量,
            width=5,
            font=("Arial", 10)
        )
        长休息输入框.pack(side='left')
        
        # 应用设置按钮
        应用设置按钮 = tk.Button(
            设置框架,
            text="应用设置",
            command=self.应用设置,
            font=("Arial", 10),
            bg='#3498DB',
            fg='white',
            relief='flat',
            cursor='hand2'
        )
        应用设置按钮.pack(pady=5)
        
        # 设置进度条样式
        样式 = ttk.Style()
        样式.theme_use('default')
        样式.configure(
            "TProgressbar",
            background='#3498DB',
            troughcolor='#2C3E50',
            bordercolor='#2C3E50',
            lightcolor='#3498DB',
            darkcolor='#3498DB'
        )
        
    def 应用设置(self):
        """应用新的时间设置"""
        try:
            self.工作时長 = int(self.工作时间变量.get())
            self.短休息时长 = int(self.短休息变量.get())
            self.长休息时长 = int(self.长休息变量.get())
            
            if not self.是否运行中:
                self.剩余时间 = self.工作时長 * 60
                self.总时间 = self.工作时長 * 60
                self.更新显示()
            
            messagebox.showinfo("设置成功", "时间设置已更新！")
        except ValueError:
            messagebox.showerror("错误", "请输入有效的数字！")
    
    def 开始计时(self):
        """开始计时"""
        if not self.是否运行中:
            self.是否运行中 = True
            self.是否暂停 = False
            self.停止事件.clear()
            self.上次更新时间 = time.time()
            
            self.开始按钮.config(state='disabled')
            self.暂停按钮.config(state='normal')
            
            if self.会话开始时间 is None:
                self.会话开始时间 = datetime.now()
            
            # 在新线程中运行计时器
            self.计时器线程 = threading.Thread(target=self.运行计时器)
            self.计时器线程.daemon = True
            self.计时器线程.start()
    
    def 暂停计时(self):
        """暂停/继续计时"""
        if self.是否运行中:
            if self.是否暂停:
                self.是否暂停 = False
                self.暂停按钮.config(text="暂停")
                self.上次更新时间 = time.time()  # 重置更新时间
            else:
                self.是否暂停 = True
                self.暂停按钮.config(text="继续")
    
    def 重置计时器(self):
        """重置计时器"""
        self.是否运行中 = False
        self.是否暂停 = False
        self.是否休息 = False
        self.停止事件.set()
        
        if self.是否休息:
            self.剩余时间 = self.短休息时长 * 60
            self.总时间 = self.短休息时长 * 60
        else:
            self.剩余时间 = self.工作时長 * 60
            self.总时间 = self.工作时長 * 60
        
        self.开始按钮.config(state='normal')
        self.暂停按钮.config(state='disabled', text="暂停")
        self.更新显示()
    
    def 运行计时器(self):
        """运行计时器"""
        logger.info("计时器线程启动")
        try:
            while not self.停止事件.is_set() and self.是否运行中:
                logger.debug(f"计时器循环 - 运行中: {self.是否运行中}, 暂停: {self.是否暂停}, 剩余时间: {self.剩余时间}")
                
                if not self.是否暂停:
                    # 使用停止事件来控制sleep，可以立即响应停止信号
                    logger.debug("等待1秒或停止信号")
                    if self.停止事件.wait(1.0):  # 等待1秒或停止信号
                        logger.debug("收到停止信号，退出循环")
                        break
                    
                    # 双重检查状态
                    if self.是否运行中 and not self.是否暂停 and not self.停止事件.is_set():
                        with self.线程锁:
                            if self.剩余时间 > 0:
                                self.剩余时间 -= 1
                                logger.debug(f"更新剩余时间: {self.剩余时间}")
                            else:
                                break
                        
                        # 使用after_idle来避免UI更新堆积
                        logger.debug("安排UI更新")
                        self.根窗口.after_idle(self.更新显示)
                else:
                    # 暂停时短暂休眠，但仍然响应停止信号
                    logger.debug("计时器暂停中，等待0.1秒")
                    if self.停止事件.wait(0.1):
                        logger.debug("暂停状态下收到停止信号")
                        break
            
            # 计时完成检查
            if self.剩余时间 == 0 and self.是否运行中 and not self.停止事件.is_set():
                logger.info("计时完成，安排计时完成处理")
                self.根窗口.after_idle(self.计时完成)
                
        except Exception as e:
            logger.error(f"计时器线程异常: {e}")
            self.停止事件.set()
            self.根窗口.after_idle(lambda: messagebox.showerror("错误", f"计时器线程异常: {e}"))
        finally:
            logger.info("计时器线程退出")
    
    def 计时完成(self):
        """计时完成"""
        self.是否运行中 = False
        # 取消UI更新定时器
        try:
            self.根窗口.after_cancel(self._ui_update_id)
        except:
            pass
        self.播放声音()
        
        if not self.是否休息:
            # 工作时间结束，进入休息
            self.番茄计数 += 1
            self.总工作时间 += self.工作时長
            
            # 添加工作记录
            备注 = self.获取备注("工作")
            if 备注 is not None:
                self.添加记录(
                    type="work",
                    duration=self.工作时長,
                    note=备注
                )
            
            # 检查是否需要长休息（每4个番茄钟）
            if self.番茄计数 % 4 == 0:
                self.剩余时间 = self.长休息时长 * 60
                self.总时间 = self.长休息时长 * 60
            else:
                self.剩余时间 = self.短休息时长 * 60
                self.总时间 = self.短休息时长 * 60
            
            self.是否休息 = True
            self.状态标签.config(text="休息时间", fg='#27AE60')
            self.时间标签.config(fg='#27AE60')
        else:
            # 休息时间结束，开始工作
            self.剩余时间 = self.工作时長 * 60
            self.总时间 = self.工作时長 * 60
            self.是否休息 = False
            self.状态标签.config(text="工作时间", fg='#3498DB')
            self.时间标签.config(fg='#E74C3C')
            
            # 添加休息记录
            备注 = self.获取备注("休息")
            if 备注 is not None:
                self.添加记录(
                    type="break",
                    duration=self.短休息时长 if self.番茄计数 % 4 != 0 else self.长休息时长,
                    note=备注
                )
        
        self.开始按钮.config(state='normal')
        self.暂停按钮.config(state='disabled', text="暂停")
        self.更新显示()
    
    def 更新显示(self):
        """更新显示"""
        with self.线程锁:
            try:
                分钟 = self.剩余时间 // 60
                秒钟 = self.剩余时间 % 60
                时间字符串 = f"{分钟:02d}:{秒钟:02d}"
                self.时间标签.config(text=时间字符串)
                
                # 更新进度条
                if self.总时间 > 0:
                    进度值 = (1 - self.剩余时间 / self.总时间) * 100
                    self.进度条['value'] = 进度值
                
                # 更新统计信息
                self.番茄标签.config(text=f"完成番茄: {self.番茄计数}")
                self.总时间标签.config(text=f"总工作时间: {self.总工作时间}分钟")
            except Exception as e:
                logger.error(f"更新显示时出错: {e}")
    
    def 播放声音(self):
        """播放提示音"""
        try:
            if platform.system() == 'Windows':
                # Windows系统播放系统声音
                winsound.MessageBeep(winsound.MB_ICONEXCLAMATION)
                time.sleep(0.5)
                winsound.MessageBeep(winsound.MB_ICONEXCLAMATION)
            else:
                # 其他系统使用终端铃声
                print('\a\a\a')
        except:
            # 如果播放失败，静默处理
            pass
            
    def 获取备注(self, 记录类型):
        """获取用户输入的备注"""
        对话框 = tk.Toplevel(self.根窗口)
        对话框.title("添加备注")
        对话框.geometry("400x220+800+200")  # 设置窗口大小
        对话框.resizable(True, True)  # 允许调整大小
        对话框.attributes('-topmost',True)  # 窗口保持在最上层
        
        # 设置窗口居中
        对话框.transient(self.根窗口)
        对话框.grab_set()
        
        # 添加到窗口管理列表
        self.打开窗口列表.append(对话框)
        
        结果 = {"备注": None}
        
        # 创建标签
        标签 = ttk.Label(
            对话框, 
            text=f"为本次【{记录类型}】添加备注(可选):",
            font=("Arial", 11)
        )
        标签.pack(pady=20, padx=20)
        
        # 创建文本输入框
        文本输入框 = tk.Text(对话框, height=5, width=40, font=("Arial", 10))
        文本输入框.pack(pady=10, padx=20)
        文本输入框.focus_set()
        
        def 确定操作():
            结果["备注"] = 文本输入框.get("1.0", tk.END).strip()
            if 对话框 in self.打开窗口列表:
                self.打开窗口列表.remove(对话框)
            对话框.destroy()
        
        def 取消操作():
            if 对话框 in self.打开窗口列表:
                self.打开窗口列表.remove(对话框)
            对话框.destroy()
        
        # 创建按钮框架
        按钮框架 = ttk.Frame(对话框)
        按钮框架.pack(pady=10)
        
        确定按钮 = ttk.Button(按钮框架, text="确定", command=确定操作, width=10)
        确定按钮.pack(side=tk.LEFT, padx=5)
        
        取消按钮 = ttk.Button(按钮框架, text="取消", command=取消操作, width=10)
        取消按钮.pack(side=tk.LEFT, padx=5)
        
        # 绑定回车键
        对话框.bind('<Return>', lambda e: 确定操作())
        对话框.bind('<Escape>', lambda e: 取消操作())
        
        # 绑定窗口关闭事件
        对话框.protocol("WM_DELETE_WINDOW", lambda: 取消操作())
        
        # 等待对话框关闭
        对话框.wait_window()
        
        return 结果["备注"]
        
    def 添加记录(self, type, duration, note=""):
        """添加一条新记录"""
        记录 = {
            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "type": type,
            "duration": duration,
            "note": note
        }
        self.记录列表.append(记录)
        self.保存记录()
        
    def 查看记录(self):
        """查看所有记录"""
        记录窗口 = tk.Toplevel(self.根窗口)
        记录窗口.title("番茄钟记录")
        记录窗口.geometry("600x400")
        记录窗口.resizable(True, True)
        
        # 添加到窗口管理列表
        self.打开窗口列表.append(记录窗口)
        
        # 绑定窗口关闭事件
        记录窗口.protocol("WM_DELETE_WINDOW", lambda: self.关闭窗口(记录窗口))
        
        # 创建滚动条
        滚动条 = ttk.Scrollbar(记录窗口)
        滚动条.pack(side=tk.RIGHT, fill=tk.Y)
        
        # 创建Treeview
        树形视图 = ttk.Treeview(
            记录窗口,
            columns=("timestamp", "type", "duration", "note"),
            show="headings",
            yscrollcommand=滚动条.set
        )
        树形视图.pack(fill=tk.BOTH, expand=True)
        
        # 配置列
        树形视图.heading("timestamp", text="时间")
        树形视图.heading("type", text="类型")
        树形视图.heading("duration", text="时长(分钟)")
        树形视图.heading("note", text="备注")
        
        树形视图.column("timestamp", width=150)
        树形视图.column("type", width=80)
        树形视图.column("duration", width=80)
        树形视图.column("note", width=290)
        
        # 添加数据
        for 记录 in reversed(self.记录列表):  # 最新记录显示在最上面
            树形视图.insert("", tk.END, values=(
                记录["timestamp"],
                "工作" if 记录["type"] == "work" else "休息",
                记录["duration"],
                记录["note"]
            ))
        
        # 配置滚动条
        滚动条.config(command=树形视图.yview)
    
    def 关闭窗口(self, 窗口):
        """关闭窗口并从管理列表中移除"""
        if 窗口 in self.打开窗口列表:
            self.打开窗口列表.remove(窗口)
        窗口.destroy()

def 主函数():
    根窗口 = tk.Tk()
    应用 = 番茄计时器(根窗口)
    根窗口.mainloop()

if __name__ == "__main__":
    主函数()
